package site.zhoulei.springbootstu.restful;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpMethod;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import site.zhoulei.springbootstu.controller.ArticleController;
import site.zhoulei.springbootstu.model.Article;
import site.zhoulei.springbootstu.service.ArticelRestService;

import javax.annotation.Resource;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;

/**
 * 用于测试MockMVC测试框架--service 真实的serlet容器测试
 * 我们的保存文章的Controller方法，调用ArticleService的saveArticle进行文章的保存。
 * @author zlleige
 * @create 2020-07-29 17:20
 */
@Slf4j //lombok日志
//@SpringBootTest
@ExtendWith({SpringExtension.class})
@AutoConfigureMockMvc
@WebMvcTest(ArticleController.class)
public class ArticleRestControllerTest3 {

    @Resource
    //创建mock对象  模拟网络请求
    private MockMvc mockMvc;

//    //初始化mock对象
//    @BeforeAll //采用junit5
//    static void setUp(){
//        //对应相应的controller对象
//        mockMvc = MockMvcBuilders.standaloneSetup(new ArticleController()).build();
//    }

    @MockBean //MockBean伪造模拟一个Service
    ArticelRestService articelRestService;

    //测试方法
    @Test
    public void saveArticle() throws Exception {
        //对应的json请求内容
        String article = "{\n" +
                "\t\"id\": 1,\n" +
                "\t\"author\": \"zlleige\",\n" +
                "\t\"title\": \"我的祖国22\",\n" +
                "\t\"content\": \"c\",\n" +
                "\t\"createTime\": \"2019-12-3 13:32:3\",\n" +
                "\t\"reader\": [{\"name\":\"leige\",\"age\":18},{\"name\":\"zz\",\"age\":23}]\n" +
                "}";

        ObjectMapper objectMapper = new ObjectMapper();
        //将json字符串转化为相应的对象
        Article  articleObj = objectMapper.readValue(article, Article.class);

        //打桩 调用articleService.saveArticle(articleObj)方法的时候，不要去真的调用这个方法，直接返回一个结果（“ok”）
        when(articelRestService.saveArticle(articleObj)).thenReturn("ok");

        //构建http请求，配置返回对象的匹配规则
        MvcResult result = mockMvc.perform(
                MockMvcRequestBuilders.request(HttpMethod.POST,"/rest/articles")
                .contentType("application/json")
                .content(article)
        )
                //添加RequsetMatcher验证规则
                .andExpect(MockMvcResultMatchers.status().isOk())
                //AjaxResonse对象中data成员的author属性为"zlleige"
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.author").value("zlleige"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.reader[0].age").value("18"))
                .andDo(print())  ////打印出请求和相应的内容
                .andReturn(); //最后返回相应的MvcResult,然后进行自定义验证/进行下一步的异步处理


//        perform : 模拟执行一个RequestBuilder构建的HTTP请求，会执行SpringMVC的流程并映射到相应的控制器Controller执行。
//        contentType：发送请求内容的序列化的格式，"application/json"表示JSON数据格式
//        andExpect: 添加RequsetMatcher验证规则，验证控制器执行完成后结果是否正确，或者说是结果是否与我们期望（Expect）的一致。
//        andDo: 添加ResultHandler结果处理器，比如调试时打印结果到控制台
//        andReturn: 最后返回相应的MvcResult,然后进行自定义验证/进行下一步的异步处理




    }
}


/**
 @ExtendWith(@RunWith注解)
 RunWith方法为我们构造了一个的Servlet容器运行运行环境，并在此环境下测试。然而为什么要构建servlet容器？因为使用了依赖注入，注入了MockMvc对象，而在上一个例子里面是我们自己new的。
 而@AutoConfigureMockMvc注解，该注解表示mockMvc对象由spring 依赖注入构建，你只负责使用就可以了。这种写法是为了让测试在servlet容器环境下执行。
 简单的说：如果你单元测试代码使用了“依赖注入@Resource”就必须加上@ExtendWith，如果你不是手动new MockMvc对象就加上@AutoConfigureMockMvc

 实际上@SpringBootTest 注解注解已经包含了 @ExtendWith注解，如果使用了前者，可以忽略后者！
 */